module sim3 (
);

reg clk, rst_n;
reg hclk;
initial begin
    clk = 1;
    forever #1 clk = ~clk;
end

initial begin
    hclk = 0;
    forever #5 hclk = ~hclk;
end

initial begin
    rst_n = 1;
    #30 rst_n = 0;
    #20 rst_n = 1;
end

wire[31:0] ho_haddr  ;
wire[2:0] ho_hburst ;
wire[1:0] ho_htrans ;
wire[31:0] ho_hwdata ;
wire ho_hwrite ;
wire[2:0] ho_hsize  ;
wire[31:0] hi_hrdata ;
wire hi_hready ;
wire[1:0] hi_hresp  ;
wire ho_hbusreq;

wire       mi1_next;
reg [31:0] mo1_wdata;
reg        mo1_wdata_valid;

reg [31:0] mo1_burst_addr;       
reg [2:0]  mo1_data_size;
reg        mo1_write;
reg        mo1_read;
reg [31:0] mo1_burst_data_num;
reg        mo1_icont;
wire[31:0] mi1_rdata;
wire[31:0] mi1_raddr;
wire       mi1_rdata_valid;

wire        we_s_w;
wire        re_s_w;
wire        cle_w;
wire        ale_w;
wire[7:0]   dq_i_w;
wire[7:0]   dq_o_w;
wire        dq_oen_w;
wire        dqs_i_w;
wire        dqs_o_w;
wire        dqs_oen_w;
wire        rb_s_w;


wire[7:0] dq_w;
assign dq_i_w = dq_w;
assign dq_w = dq_oen_w ? dq_o_w : 8'hzz;

wire dqs_w;
assign dqs_i_w = dqs_w;
assign dqs_w = dqs_oen_w ? dqs_o_w : 1'bz;

NC_AHB_wrapper u_NC_AHB_wrapper(
	.mclk    (clk    ),

    .HCLK    (hclk    ),
    .HRESTn  (rst_n  ),
    .HADDR   (ho_haddr   ),
    .HWRITE  (ho_hwrite  ),
    .HSIZE   (ho_hsize   ),
    .HBURST  (ho_hburst  ),
    .HTRANS  (ho_htrans  ),
    .HWDATA  (ho_hwdata  ),
    .HREADY  (hi_hready  ),
    .HRESP   (hi_hresp   ),
    .HRDATA  (hi_hrdata  ),

    .CEn     (CEn     ),
    .WPn     (WPn     ),
    .WE           (we_s_w         ),
    .RE           (re_s_w         ),
    .CLE          (cle_w          ),
    .ALE          (ale_w          ),
    .DQ_i         (dq_i_w         ),
    .DQ_o         (dq_o_w         ),
    .DQ_oen       (dq_oen_w       ),
    .DQS_i        (dqs_i_w        ),
    .DQS_o        (dqs_o_w        ),
    .DQS_oen      (dqs_oen_w      ),
    .RB           (rb_s_w         )
);

ahb_master u_ahb_master(
	.i_hclk     (hclk       ),
    .i_hreset_n (rst_n      ),
    .o_haddr    (ho_haddr    ),
    .o_hburst   (ho_hburst   ),
    .o_htrans   (ho_htrans   ),
    .o_hwdata   (ho_hwdata   ),
    .o_hwrite   (ho_hwrite   ),
    .o_hsize    (ho_hsize    ),
    .i_hrdata   (hi_hrdata   ),
    .i_hready   (hi_hready   ),
    .i_hresp    (hi_hresp    ),
    .i_hgrant   (1           ),
    .o_hbusreq  (ho_hbusreq  ),

    .o_next     (mi1_next             ),
    .i_data     (mo1_wdata            ),
    .i_dav      (mo1_wdata_valid      ),
    .i_addr     (mo1_burst_addr       ),
    .i_size     (mo1_data_size        ),
    .i_wr       (mo1_write            ),
    .i_rd       (mo1_read             ),
    .i_min_len  (mo1_burst_data_num   ),
    .i_cont     (mo1_icont            ),
    .o_data     (mi1_rdata            ),
    .o_addr     (mi1_raddr            ),
    .o_dav      (mi1_rdata_valid      )
);

nand_model u_nand_model(
    .Dq_Io     (dq_w     ),
    .Dqs       (dqs_w    ),
    .Cle       (cle_w    ),
    .Ale       (ale_w    ),
    .Ce_n      (0        ),
    .Clk_We_n  (we_s_w   ),
    .Wr_Re_n   (re_s_w   ),
    .Wp_n      (1        ),
    .Rb_n      (rb_s_w   ),
    .ENi       (1        ),
    .ENo       (         ),
    .Dqs_c     (         ),
    .Re_c      (1'b1     )
);
reg[31:0] tempAddr;

reg c1;
wire c1c;
assign c1c = hclk & c1;

reg c2;
reg c2k;
wire c2c;
assign c2c = hclk & c2k;

reg c3;
wire c3c;
assign c3c = hclk & c3;

reg c4;
reg c4k;
wire c4c;
assign c4c = hclk & c4k;

reg c5;
wire c5c;
assign c5c = hclk & c5;

reg c6;
wire c6c;
assign c6c = hclk & c6;

reg c7;
wire c7c;
assign c7c = c7 ? hclk : 1;

/************************** reset controler**********************/
always @(posedge c1c or negedge rst_n) begin
    if (!rst_n) begin
        c1 <= 1;
        c2 <= 0;
        mo1_data_size <= 3'b010;
        mo1_burst_data_num <= 8'hf0;
        mo1_icont <= 0;
        tempAddr <= 32'h0200_0014;
        mo1_burst_addr <= 32'h0200_0014;
        mo1_wdata_valid <= 0;
        mo1_write <= 0;
        mo1_read <= 0;
    end else begin
        if (mo1_burst_addr > 32'h0200_0000 ) begin
            mo1_burst_addr <= tempAddr;
            tempAddr <= tempAddr - 4;
            mo1_wdata_valid <= 1;
            mo1_write <= 1;
            case (tempAddr)
                32'h0200_0014 : mo1_wdata <= {16'd10, 16'd40};
                //set PSR     ecc_clk = mclk / 16'd10; data_clk = mclk / 16'd40; rw_clk = data_clk / 2;
                32'h0200_0010 : mo1_wdata <= 32'h0;
                //set AR1 = 0
                32'h0200_000c : mo1_wdata <= 32'h0;
                //set AR2 = 0
                32'h0200_0008 : mo1_wdata <= {16'd1024, 16'd128};
                //set PSR page_size = 1024byte; ecc_size = 128byte; 
                32'h0200_0004 : mo1_wdata <= 32'h0;
                //FLR is read only
                32'h0200_0000 : mo1_wdata <= {4'd3, 4'd2, 8'd7, 9'd0, 1'b1, 1'b1, 1'b1, 1'b1, 3'b111};
                //set CR block_address_number = 3byte; page_address_number = 2bute;
                //       dll_dly = 7 * half clock period, dll_on = 1;
                //       EN = 1; decode_en = 1; code_en = 1;
                //       cmd = 3'b111(reset NANDflash)
                default: mo1_wdata <= mo1_wdata;
            endcase
        end else begin
            c1 <= 0;
            c2 <= 1;
        end
    end
end
/************************** tiny delay**********************/
// wait write finish;
always @(*) begin
   if (c2 == 1) begin
       #300 
       if (rst_n == 1) begin
           c2k = 1;
       end else begin
           c2k = 0;
       end
   end else begin
       c2k = 0;
   end 
end
/************************** set features on features reg 0x01(timing mode)**********************/
reg c2flg;
always @(posedge c2c or negedge rst_n) begin
    if (!rst_n) begin
        c3 <= 0;
        c2flg <= 0;
    end else begin
        if (c2flg == 1) begin
            if (mo1_burst_addr > 32'h0200_0000) begin//(3) start write a new command 
                mo1_read <= 0;
                mo1_write <= 1;
                mo1_wdata_valid <= 1;
                case (mo1_burst_addr)
                   32'h0200_000a : begin
                        mo1_burst_addr <= 32'h0200_000c; 
                        mo1_wdata <= {24'h0, 8'h01};
                        //(4)set features address on 8'h01(timing mode) on AR1
                   end
                   32'h0200_000c : begin
                        mo1_burst_addr <= 32'h0200_0000;
                        mo1_wdata <= {4'd3, 4'd2, 8'd7, 9'd0, 1'b1, 1'b1, 1'b1, 1'b1, 3'b110};
                        //(5)set new command is set_features
                   end
                    default: ;
                endcase
            end else begin
                mo1_wdata_valid <= 0;
                c2 <= 0;
                c3 <= 1;
            end
        end else if (mi1_rdata_valid && mi1_rdata[4] == 1) begin //(2) if command is finish
            c2flg <= 1;
            mo1_burst_addr <= 32'h0200_000a;
        end else begin  // (1) read FLR to judge command is finish 
            mo1_write <= 0;
            mo1_read <= 1;
            mo1_burst_addr <= 32'h0200_0004;
        end
    end
end

/************************** set timing mode to DDR2 or other**********************/
always @(posedge c3c or negedge rst_n) begin
    if (!rst_n) begin
        c4 <= 0;
    end else begin
        if (mi1_rdata_valid && mi1_rdata[1] == 1) begin//(2) if FLR[1] == 1(can write data to flash controler)
            mo1_read <= 0;
            mo1_write <= 1;
            mo1_wdata_valid <= 1;
            mo1_burst_addr <= 32'h0200_0018;
            mo1_wdata <= {24'h0, 8'b0010_0000}; 
            //(3) write features data 8'b0010_0000(DDR2 mode 0) to flash timing reg
            //also can write 8'b0000_0000(SDR mode 0) and 8'b0010_0000(DDR mode0)
            c3 <= 0;
            c4 <= 1;
        end else begin// (1)send address to read FLR
            mo1_write <= 0;
            mo1_read <= 1;
            mo1_wdata_valid <= 0;
            mo1_burst_addr <= 32'h0200_0004; 
        end
    end
end
/************************** tiny delay**********************/
//wait write finish 
always @(*) begin
    if (c4 == 1 && c3 == 0) begin
        # 10 mo1_wdata_valid <= 0;
        #200 c4k = 1;
    end else begin
        c4k = 0;
    end
end
/************************** send write page command**********************/
reg c4flg;
always @(posedge c4c or negedge rst_n) begin
    if (!rst_n) begin
        c5 <= 0;
        c4flg <= 0;
    end else begin
        if (c4flg == 1) begin
            if (mo1_burst_addr > 32'h0200_0000) begin
                mo1_read <= 0;
                mo1_write <= 1;
                mo1_wdata_valid <= 1;
                case (mo1_burst_addr)
                   32'h0200_000a : begin
                        mo1_burst_addr <= 32'h0200_000c;
                        mo1_wdata <= 32'h0200_000c;
                        //(3)set AR1 = 32'h0200_000c;
                   end
                   32'h0200_000c : begin
                        mo1_burst_addr <= 32'h0200_0010;
                        mo1_wdata <= 32'h0;
                        //(4)set AR2 = 32'h0
                   end
                   32'h0200_0010 : begin
                        mo1_burst_addr <= 32'h0200_0000;
                        mo1_wdata <= {4'd3, 4'd2, 8'd7, 9'd0, 1'b1, 1'b1, 1'b1, 1'b1, 3'b010};
                        //(5)set new command to (3'b010)(write_page)
                   end
                    default: ;
                endcase
            end else begin
                mo1_wdata_valid <= 0;
                c4 <= 0;
                #100 c5 <= 1;
            end
        end else if (mi1_rdata_valid && mi1_rdata[4] == 1) begin//(2) if last command is finish 
            c4flg <= 1;//write new command
            mo1_burst_addr <= 32'h0200_000a;
        end else begin //(1) read FLR to judge last command is finished or not 
            mo1_write <= 0;
            mo1_read <= 1;
            mo1_burst_addr <= 32'h0200_0004;
        end
    end
end

reg[7:0] tre0;
reg[7:0] tre1;
reg[7:0] tre2;
reg[7:0] tre3;
/********************send data to write page and waite command finish**********************/
always @(posedge c5c or negedge rst_n) begin
    if (!rst_n) begin
        tre0 <= 0;
        tre1 <= 1;
        tre2 <= 2;
        tre3 <= 3;
        c6 <= 0;
    end else begin
        //(2) is FLR[1] == 1(if can write data to WDR or not)
        if (mi1_rdata_valid == 1 && mi1_rdata[1] == 1) begin
            mo1_write <= 1;
            mo1_read <= 0;
            mo1_wdata_valid <= 1;
            //send data to WDR
            mo1_burst_addr <= 32'h0200_0018;
            mo1_wdata <= {tre0, tre1, tre2, tre3};
            tre0 <= tre0 + 4;
            tre1 <= tre1 + 4;
            tre2 <= tre2 + 4;
            tre3 <= tre3 + 4;
        // is FLR[4] == 1 (if write data finish )
        end else if (mi1_rdata_valid == 1 && mi1_rdata[4] == 1) begin
            c5 <= 0;
            c6 <= 1;
            mo1_write <= 0;
            mo1_read <= 0;
            mo1_burst_addr <= 32'h0200_000a;
        end else begin //(1)read FLR 
            mo1_write <= 0;
            mo1_read <= 1;
            mo1_wdata_valid <= 0;
            mo1_burst_addr <= 32'h0200_0004;
        end
    end
end
/************************** send read page command **********************/
always @(posedge c6c or negedge rst_n) begin
    if (!rst_n) begin
        c7 <= 0;
    end else begin
        if (mo1_burst_addr > 32'h0200_0000) begin
            mo1_read <= 0;
            mo1_write <= 1;
            mo1_wdata_valid <= 1;
            case (mo1_burst_addr)
                32'h0200_000a : begin
                     mo1_burst_addr <= 32'h0200_000c;
                     mo1_wdata <= 32'h0200_000c;
                     //(1)set AR1 = 32'h0200_000c; 
                end
                32'h0200_000c : begin
                     mo1_burst_addr <= 32'h0200_0010;
                     mo1_wdata <= 32'h0;
                     //(2)set AR2 = 32'h0
                end
                32'h0200_0010 : begin
                     mo1_burst_addr <= 32'h0200_0000;
                     mo1_wdata <= {4'd3, 4'd2, 8'd7, 9'd0, 1'b1, 1'b1, 1'b1, 1'b1, 3'b001};
                     //(3)set new command to (3'b001)(read page)
                end
                 default: ;
            endcase
        end else begin
            mo1_wdata_valid <= 0;
            mo1_read <= 0;
            mo1_write <= 0;
            c6 <= 0;
            c7 <= 1;
        end
    end
end

reg read_state;
reg readed;

/************************** read RDR and FLR on AHBlite bus**************************/
reg[31:0] readed_data;
reg[31:0] readed_data_num;
always @(posedge c7c or negedge rst_n) begin
    if (!rst_n) begin
        readed <= 1;
    end else begin
        mo1_wdata_valid <= 0;
        if (mi1_rdata_valid == 1 && mi1_raddr == 32'h0200_0004) begin
            if (mi1_rdata[0] == 1) begin//(2) if can read data form RDR
                mo1_read <= 1;
                mo1_write <= 0;
                mo1_burst_addr <= 32'h0200_001c;//(3)read data form RDR 
                readed <= 1;
                //(4) if read finish 
            end else if(readed_data_num > 32'd1024) begin
                readed <= 0;
                mo1_read <= 0;
                mo1_write <= 0;
                c7 <= 0;
            end else begin
                readed <= 0;
                mo1_read <= 1;
                mo1_write <= 0;
                mo1_burst_addr <= 32'h0200_0004;
            end
        end else if(readed == 1)begin //(1)read FLR
            readed <= 0;
            mo1_read <= 1;
            mo1_write <= 0;
            mo1_burst_addr <= 32'h0200_0004;
        end else begin
            mo1_read <= 0;
            mo1_write <= 0;
        end
    end
end


/**************read valid data on AHBlite receive***************/
always @(negedge c7c or negedge rst_n) begin
    if (!rst_n) begin
        readed_data <= 0;
        readed_data_num <= 0;
    end else begin
        //if readed address = RDR and valid
        if (mi1_rdata_valid == 1 && mi1_raddr == 32'h0200_001c) begin
            readed_data <= mi1_rdata;//save data
            readed_data_num <= readed_data_num + 1;//counter + 1
        end else begin
            readed_data <= readed_data;
            readed_data_num <= readed_data_num;
        end        
    end
end

endmodule